package easycontroller.plugin.expand;

import easycontroller.plugin.Plugin;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;

/*

    路由规则

    key写成ant风格表达式

    如 GET /test/**


    value 写入 处理方式,json格式

 {
  time:'default',// 处理时期 default/after/before
  target:'http://www.baidu.com' //跳转路径
 }



 */
//提供网关操作
public class GatewayPlugin extends Plugin {

    {
        setName("gateway");
    }

    private RestTemplate restTemplate;

    {
        restTemplate = new RestTemplate();

        restTemplate.setErrorHandler(new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse response) throws IOException {
                return false;
            }

            @Override
            public void handleError(ClientHttpResponse response) throws IOException {

            }
        });
    }

    private List<Handler> bs = new ArrayList<>();
    private List<Handler> as = new ArrayList<>();
    private List<Handler> ds = new ArrayList<>();

    @Override
    public void start() {

        if (config != null) {
            for (String s : config.keySet()) {
                String v = config.get(s);
                Handler parse = Handler.parse(s, v);
                if (parse == null) {
                    System.err.println("无效配置:" + s + "-" + v);
                    return;
                }

                switch (parse.time) {
                    case "after":
                        as.add(parse);
                        break;
                    case "before":
                        bs.add(parse);
                        break;
                    default:
                        ds.add(parse);
                        break;
                }

            }
        }

        as.sort(Comparator.comparingDouble(a -> a.rate));
        bs.sort(Comparator.comparingDouble(a -> a.rate));
        ds.sort(Comparator.comparingDouble(a -> a.rate));

    }


    @Override
    public void stop() {
    }

    @Override
    public void restart() {
        bs = new ArrayList<>();
        as = new ArrayList<>();
        ds = new ArrayList<>();
        start();
    }

    public boolean onBefore(HttpServletRequest request, HttpServletResponse response) {
        String uri = request.getRequestURI();
        String method = request.getMethod();
        for (Handler b : bs) {
            if ((method.equals(b.method) || b.method.equals("*")) && uri.matches(b.regex)) {
                handle(request, response, b);
                return false;
            }
        }
        return true;
    }

    public boolean onAfter(HttpServletRequest request, HttpServletResponse response) {
        String uri = request.getRequestURI();
        String method = request.getMethod();
        for (Handler b : as) {
            if ((method.equals(b.method) || b.method.equals("*")) && uri.matches(b.regex)) {
                handle(request, response, b);
                return false;
            }
        }
        return true;
    }

    public boolean onDefault(HttpServletRequest request, HttpServletResponse response) {
        String uri = request.getRequestURI();
        String method = request.getMethod();
        for (Handler b : ds) {
            if ((method.equals(b.method) || b.method.equals("*")) && uri.matches(b.regex)) {
                handle(request, response, b);
                return false;
            }
        }
        return true;
    }


    private void handle(HttpServletRequest request, HttpServletResponse response, Handler b) {


        //这里请求参数没有??
        String uri = request.getRequestURI();
        String method = request.getMethod();
        String queryString = request.getQueryString();

        //新路径
        String target = b.target;
        if (b.index != -1) {
            target += uri.substring(b.index);
        }

        if (queryString != null) {
            target += "?" + queryString;
        }

        restTemplate.execute(target, HttpMethod.valueOf(method), req -> {
            Enumeration<String> headerNames = request.getHeaderNames();
            HttpHeaders reqHeaders = req.getHeaders();
            while (headerNames.hasMoreElements()) {
                String s = headerNames.nextElement();
                String header = request.getHeader(s);
                reqHeaders.set(s, header);
            }
            OutputStream outputStream = req.getBody();
            ServletInputStream inputStream = request.getInputStream();
            StreamUtils.copy(inputStream, outputStream);
        }, resp -> {
            response.setStatus(resp.getStatusCode().value());
            HttpHeaders headers = resp.getHeaders();
            for (String key : headers.keySet()) {
                List<String> list = headers.get(key);
                if (list != null && list.size() > 0) {
                    for (String s : list) {
                        response.addHeader(key, s);
                    }
                }


            }

            InputStream body = resp.getBody();
            ServletOutputStream outputStream = response.getOutputStream();
            StreamUtils.copy(body, outputStream);
            return null;
        });


    }


    private static class Handler {

        private String regex;

        private String method;

        private String time;

        private double rate;

        private String target;

        private int index;


        private static List<String> allow = new ArrayList<>();
        private static List<String> timeAllow = new ArrayList<>();

        static {
            allow.add("*");
            allow.add("GET");
            allow.add("POST");
            allow.add("DELETE");
            allow.add("PUT");

            timeAllow.add("after");
            timeAllow.add("before");
            timeAllow.add("default");

        }

        private static Handler parse(String key, String value) {

            if (key == null) {
                return null;
            }

            key = key.trim();

            if (key.equals("") || value == null) {
                return null;
            }

            String[] split = key.split(" ");

            if (split.length != 2) {
                return null;
            }

            String uri = split[1];

            //(\*\*)
            if (!uri.matches("^(/[a-zA-Z0-9._]*)*(/\\*\\*)?$")) {
                return null;
            }

            String method = split[0];

            if (!allow.contains(method)) {
                return null;
            }

            JSONObject jsonObject;

            try {
                jsonObject = new JSONObject(value);
            } catch (JSONException e) {
                return null;
            }

            String time;
            String to;
            try {
                time = (String) jsonObject.get("time");
                if (!timeAllow.contains(time)) {
                    return null;
                }
                to = (String) jsonObject.get("target");
                if (to == null) {
                    return null;
                }
            } catch (JSONException e) {
                return null;
            }

            Handler handler = new Handler();
            handler.regex = uriToRegex(uri);
            handler.method = method;
            handler.time = time;
            handler.target = to;
            char[] chars = uri.toCharArray();
            int n = 0;
            for (char aChar : chars) {
                if (aChar == '*') {
                    n++;
                }
            }
            handler.rate = n * 1.0 / chars.length;
            handler.index = uri.indexOf("/**");
            return handler;
        }

        private static String uriToRegex(String uri) {
            uri = uri.replace("/**", "(/[^/]*)*");
            uri = uri.replace("/*", "/[^/]*");
            return uri;
        }


    }


}
